package com.dao;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.lang.time.DateUtils;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.bean.HistoryLog;
import com.bean.ParameterBean;
import com.util.DateUtil;
import com.util.Page;

public class HistoryLogDAO extends HibernateDaoSupport {

	public final static String String_op_equals = "=";
	public final static String String_op_notEquals = "<>";
	public final static String String_op_beginWith = "like";
	public final static String String_op_notBeginWith = "not like";

	public final static String Date_op_on = "on";
	public final static String Date_op_before = "before";
	public final static String Date_op_after = "after";
	public final static String Date_op_withinLast = "withinLast";
	public final static String Date_op_today = "today";
	public final static String Date_op_yesterday = "yesterday";
	public final static String Date_op_thisWeek = "thisWeek";
	public final static String Date_op_thisMonth = "thisMonth";
	public final static String Date_op_never = "never";
	public final static String Date_op_between = "between";

	public final static String Date_op_withinLast_Unit_Days = "days";
	public final static String Date_op_withinLast_Unit_Weeks = "weeks";
	public final static String Date_op_withinLast_Unit_Months = "months";

	public final static String OrderBy_Property_startTime = "startTime";
	public final static String OrderBy_Property_protocol = "protocol";
	public final static String OrderBy_Property_ipAddress = "ipAddress";
	public final static String OrderBy_Property_macAddress = "macAddress";
	public final static String OrderBy_Property_hostName = "hostName";
	public final static String OrderBy_Property_event = "event";
	private final static List<String> list = new ArrayList<String>();
	static {
		list.add(OrderBy_Property_startTime);
		list.add(OrderBy_Property_protocol);
		list.add(OrderBy_Property_ipAddress);
		list.add(OrderBy_Property_macAddress);
		list.add(OrderBy_Property_hostName);
		list.add(OrderBy_Property_event);
	}

	public final static String OrderBy_Asc = "asc";
	public final static String OrderBy_Desc = "desc";

	private static final Map<String, String> Java_Table_map = new HashMap<String, String>();
	static {
		Java_Table_map.put("ipAddress", "IpAddress");
		Java_Table_map.put("macAddress", "MacAddress");
		Java_Table_map.put("DUID", "DUID");
		Java_Table_map.put("hostName", "HostName");
		Java_Table_map.put("userName", "UserName");
	}

	public int count(ParameterBean bean) {
		Criteria criteria = getSession().createCriteria(HistoryLog.class);

		this.appendCriteria(criteria, bean);

		return Integer.parseInt(criteria.setProjection(Projections.rowCount())
				.uniqueResult().toString());
	}

	/**
	 * 
	 * @param li_op
	 * @param leaseIssue
	 * @param li_unit
	 * @param protocol
	 * @param ip_op
	 * @param ipAddress
	 * @param mac_op
	 * @param macAddress
	 * @param duid_op
	 * @param duid
	 * @param host_op
	 * @param hostName
	 * @param action
	 * @param user_op
	 * @param userName
	 * @param start_op
	 * @param startTime
	 * @param start_unit
	 * @param stop_op
	 * @param stopTime
	 * @param stop_unit
	 * @param mi_op
	 * @param memberIpAddress
	 * @param orderBy
	 * @param orderSort
	 * @param page
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public List<HistoryLog> find(ParameterBean bean, Page page) {

		Criteria criteria = getSession().createCriteria(HistoryLog.class);

		this.appendCriteria(criteria, bean);

		// criteria.setProjection(Projections.rowCount()).uniqueResult();
		// criteria.setProjection(null);

		String orderBy = bean.getOrderBy();
		if (StringUtils.isEmpty(orderBy) || !list.contains(orderBy)) {
			orderBy = OrderBy_Property_startTime;
		}

		if (OrderBy_Desc.equals(bean.getOrderSort())) {
			criteria.addOrder(Order.desc(orderBy));
		} else {
			criteria.addOrder(Order.asc(orderBy));
		}

		if (page != null) {
			criteria.setFirstResult(page.getBeginIndex());
			criteria.setMaxResults(page.getEveryPage());
		} else {
			criteria.setFirstResult(0);
			criteria.setMaxResults(Page.Page_Size_20);
		}

		return criteria.list();
	}
	
	@SuppressWarnings("unchecked")
	public List<Object[]> find(ParameterBean bean) {
		
		StringBuffer hql = new StringBuffer("select startTime,endTime,ipAddress,macAddress,hostName,memberOrServer,event,userName,protocol,DUID from HistoryLog where 1=1");
		List args = new ArrayList();
		
		// leaseIssue
		this.appendDate(hql, args, "startTime", bean.getLeaseIssueOp(), bean.getLeaseIssue(), bean.getLeaseIssueUnit(), bean.getLeaseIssueAnother());
		
		// Protocol
		this.appendString(hql, args, "protocol", String_op_equals, bean.getProtocol());
		// ipAddress
		this.appendString(hql, args, "ipAddress", bean.getIpAddressOp(), bean.getIpAddress());
		// macAddress
		this.appendString(hql, args, "macAddress", bean.getMacAddressOp(), bean.getMacAddress());
		// DUID
		this.appendString(hql, args, "DUID", bean.getDuidOp(), bean.getDuid());
		// hostName
		this.appendString(hql, args, "hostName", bean.getHostNameOp(), bean.getHostName());
		// event
		this.appendString(hql, args, "event", String_op_equals, bean.getAction());
		// userName
		this.appendString(hql, args, "userName", bean.getUserNameOp(), bean.getUserName());

		// startTime
		this.appendDate(hql, args, "startTime", bean.getStartTimeOp(), bean.getStartTime(), bean.getStartTimeUnit(), bean.getStartTimeAnother());
		// endTime
		this.appendDate(hql, args, "endTime", bean.getStopTimeOp(), bean.getStopTime(), bean.getStopTimeUnit(), bean.getStopTimeAnother());

		// memberOrServer
		this.appendString(hql, args, "memberOrServer", bean.getMemberIpAddressOp(), bean.getMemberIpAddress());
		
		String orderBy = bean.getOrderBy();
		if (StringUtils.isEmpty(orderBy) || !list.contains(orderBy)) {
			orderBy = OrderBy_Property_startTime;
		}

		hql.append(" order by ").append(orderBy).append(" ");
		
		if (OrderBy_Desc.equals(bean.getOrderSort())) {
			hql.append(OrderBy_Desc);
		} else {
			hql.append(OrderBy_Asc);
		}
		
		Query query = getSession().createQuery(hql.toString());
		query.setMaxResults(1000000);
		
		if (args != null || args.size() > 0) {
			for (int i=0; i< args.size(); i++) {
				query.setParameter(i, args.get(i));
			}
		}

		return query.list();
	}

	private void appendCriteria(Criteria criteria, ParameterBean bean) {

		// leaseIssue
		this.appendDate(criteria, "startTime", bean.getLeaseIssueOp(), bean.getLeaseIssue(), bean.getLeaseIssueUnit(), bean.getLeaseIssueAnother());

		// Protocol
		this.appendString(criteria, "protocol", String_op_equals, bean.getProtocol());
		// ipAddress
		this.appendString(criteria, "ipAddress", bean.getIpAddressOp(), bean.getIpAddress());
		// macAddress
		this.appendString(criteria, "macAddress", bean.getMacAddressOp(), bean.getMacAddress());
		// DUID
		this.appendString(criteria, "DUID", bean.getDuidOp(), bean.getDuid());
		// hostName
		this.appendString(criteria, "hostName", bean.getHostNameOp(), bean.getHostName());
		// event
		this.appendString(criteria, "event", String_op_equals, bean.getAction());
		// userName
		this.appendString(criteria, "userName", bean.getUserNameOp(), bean.getUserName());

		// startTime
		this.appendDate(criteria, "startTime", bean.getStartTimeOp(), bean.getStartTime(), bean.getStartTimeUnit(), bean.getStartTimeAnother());
		// endTime
		this.appendDate(criteria, "endTime", bean.getStopTimeOp(), bean.getStopTime(), bean.getStopTimeUnit(), bean.getStopTimeAnother());

		// memberOrServer
		this.appendString(criteria, "memberOrServer", bean.getMemberIpAddressOp(), bean.getMemberIpAddress());
	}

	private void appendString(Criteria criteria, String propertyName,
			String op, String value) {

		if (StringUtils.isNotEmpty(propertyName) && StringUtils.isNotEmpty(op)
				&& StringUtils.isNotEmpty(value)) {

			if (String_op_equals.equals(op)) {
				criteria.add(Restrictions.eq(propertyName, value));
			} else if (String_op_notEquals.equals(op)) {
				criteria.add(Restrictions.ne(propertyName, value));
			} else if (String_op_beginWith.equals(op)) {
				criteria.add(Restrictions.like(propertyName, value,
						MatchMode.START));
			} else if (String_op_notBeginWith.equals(op)) {
				criteria.add(Restrictions.sqlRestriction(Java_Table_map
						.get(propertyName)
						+ " not like '" + value + "%'"));
			}
		}
	}

	/**
	 * 
	 * @param hql
	 * @param args
	 * @param name
	 * @param op
	 * @param value
	 */
	 private void appendString(StringBuffer hql, List<Object> args, String propertyName, String op, String value) {
		 
		 if (StringUtils.isNotEmpty(propertyName) && StringUtils.isNotEmpty(op)
					&& StringUtils.isNotEmpty(value)) {
		 
			 if (String_op_equals.equals(op)) {
				 hql.append(" and ").append(propertyName);
				 hql.append(" = ?");
				 args.add(value);
			 } else if (String_op_notEquals.equals(op)) {
				 hql.append(" and ").append(propertyName);
				 hql.append(" <> ?");
				 args.add(value);
			 } else if (String_op_beginWith.equals(op)) {
				 hql.append(" and ").append(propertyName);
				 hql.append(" like ?");
				 args.add(value + "%");
			 } else if (String_op_notBeginWith.equals(op)) {
				 hql.append(" and ").append(propertyName);
				 hql.append(" not like ?");
				 args.add(value + "%");
			 }
		 }
	 }
	 
	private void appendDate(Criteria criteria, String propertyName, String op,
			String value, String value_unit, String anotherValue) {

		if (StringUtils.isNotEmpty(op)) {

			if (Date_op_on.equals(op) && StringUtils.isNotEmpty(value)) {

				Date date1 = DateUtil.str2date(value);
				Date date2 = DateUtils.addDays(date1, 1);
				criteria.add(Restrictions.ge(propertyName, operateUtcTime(date1)));
				criteria.add(Restrictions.lt(propertyName, operateUtcTime(date2)));

			} else if (Date_op_before.equals(op)
					&& StringUtils.isNotEmpty(value)) {

				criteria.add(Restrictions.lt(propertyName, operateUtcTime(DateUtil
						.str2date(value))));

			} else if (Date_op_after.equals(op)
					&& StringUtils.isNotEmpty(value)) {

				criteria.add(Restrictions.gt(propertyName, operateUtcTime(DateUtils.addDays(
						DateUtil.str2date(value), 1))));

			} else if (Date_op_withinLast.equals(op)
					&& StringUtils.isNotEmpty(value)
					&& NumberUtils.isDigits(value)
					&& StringUtils.isNotEmpty(value_unit)) {

				Date date = null;
				if (Date_op_withinLast_Unit_Days.equals(value_unit)) {
					date = DateUtils.addDays(DateUtil.today(), 0 - NumberUtils
							.toInt(value));
				} else if (Date_op_withinLast_Unit_Weeks.equals(value_unit)) {
					date = DateUtils.addWeeks(DateUtil.today(), 0 - NumberUtils
							.toInt(value));
				} else if (Date_op_withinLast_Unit_Months.equals(value_unit)) {
					date = DateUtils.addMonths(DateUtil.today(),
							0 - NumberUtils.toInt(value));
				}

				criteria.add(Restrictions.ge(propertyName, operateUtcTime(date)));

			} else if (Date_op_today.equals(op)) {

				Date date1 = DateUtil.today();
				Date date2 = DateUtils.addDays(date1, 1);
				criteria.add(Restrictions.ge(propertyName, operateUtcTime(date1)));
				criteria.add(Restrictions.lt(propertyName, operateUtcTime(date2)));

			} else if (Date_op_yesterday.equals(op)) {

				Date date2 = DateUtil.today();
				Date date1 = DateUtils.addDays(date2, -1);
				criteria.add(Restrictions.ge(propertyName, operateUtcTime(date1)));
				criteria.add(Restrictions.lt(propertyName, operateUtcTime(date2)));

			} else if (Date_op_thisWeek.equals(op)) {

				Date date1 = DateUtil.sundayOfWeek(new Date());
				Date date2 = DateUtils.addWeeks(date1, 1);
				criteria.add(Restrictions.ge(propertyName, operateUtcTime(date1)));
				criteria.add(Restrictions.lt(propertyName, operateUtcTime(date2)));

			} else if (Date_op_thisMonth.equals(op)) {

				Date date1 = DateUtil.firstDayOfMonth(new Date());
				Date date2 = DateUtils.addMonths(date1, -1);
				criteria.add(Restrictions.ge(propertyName, operateUtcTime(date2)));
				criteria.add(Restrictions.lt(propertyName, operateUtcTime(date1)));

			} else if (Date_op_never.equals(op)) {
				criteria.add(Restrictions.isNull(propertyName));
			} else if (Date_op_between.equals(op) && StringUtils.isNotEmpty(value) && StringUtils.isNotEmpty(anotherValue)) {
				Date date1 = DateUtil.str2date(value);
				Date date2 = DateUtils.addDays(DateUtil.str2date(anotherValue), 1);
				criteria.add(Restrictions.ge(propertyName, operateUtcTime(date1)));
				criteria.add(Restrictions.lt(propertyName, operateUtcTime(date2)));
			}
		}
	}

	/**
	 * 
	 * @param hql
	 * @param args
	 * @param name
	 * @param op
	 * @param value
	 * @param value_unit
	 */
	 private void appendDate(StringBuffer hql, List<Object> args, String propertyName, String op, String value, String value_unit, String anotherValue) {
		 if (StringUtils.isNotEmpty(op)) {
			 if (Date_op_on.equals(op) && StringUtils.isNotEmpty(value)) {
			
				 hql.append(" and ").append(propertyName).append(" >= ? and ").append(propertyName).append(" < ?");
				
				 Date date1 = DateUtil.str2date(value);
				 Date date2 = DateUtils.addDays(date1, 1);
				
				 args.add(operateUtcTime(date1));
				 args.add(operateUtcTime(date2));
			
			 } else if (Date_op_before.equals(op) && StringUtils.isNotEmpty(value)) {
			
				 hql.append(" and ").append(propertyName).append(" < ?");
				
				 args.add(operateUtcTime(DateUtil.str2date(value)));
			
			 } else if (Date_op_after.equals(op) && StringUtils.isNotEmpty(value)) {
			
				 hql.append(" and ").append(propertyName).append(" >= ?");
				
				 args.add(operateUtcTime(DateUtils.addDays(DateUtil.str2date(value), 1)));
			
			 } else if (Date_op_withinLast.equals(op)
						&& StringUtils.isNotEmpty(value)
						&& NumberUtils.isDigits(value)
						&& StringUtils.isNotEmpty(value_unit)) {
				 Date date = null;
				 if (Date_op_withinLast_Unit_Days.equals(value_unit)) {
					 date = DateUtils.addDays(DateUtil.today(), 0 - NumberUtils.toInt(value));
				 } else if (Date_op_withinLast_Unit_Weeks.equals(value_unit)) {
					 date = DateUtils.addWeeks(DateUtil.today(), 0 - NumberUtils.toInt(value));
				 } else if (Date_op_withinLast_Unit_Months.equals(value_unit)) {
					 date = DateUtils.addMonths(DateUtil.today(), 0 - NumberUtils.toInt(value));
				 }

				 hql.append(" and ").append(propertyName).append(" >= ?");
				 args.add(operateUtcTime(date));
			 } else if (Date_op_today.equals(op)) {

					Date date1 = DateUtil.today();
					Date date2 = DateUtils.addDays(date1, 1);
					
					hql.append(" and ").append(propertyName).append(" >= ? and ").append(propertyName).append(" < ?");
					args.add(operateUtcTime(date1));
					args.add(operateUtcTime(date2));

				} else if (Date_op_yesterday.equals(op)) {

					Date date2 = DateUtil.today();
					Date date1 = DateUtils.addDays(date2, -1);

					hql.append(" and ").append(propertyName).append(" >= ? and ").append(propertyName).append(" < ?");
					args.add(operateUtcTime(date1));
					args.add(operateUtcTime(date2));

				} else if (Date_op_thisWeek.equals(op)) {

					Date date1 = DateUtil.sundayOfWeek(new Date());
					Date date2 = DateUtils.addWeeks(date1, 1);
					hql.append(" and ").append(propertyName).append(" >= ? and ").append(propertyName).append(" < ?");
					args.add(operateUtcTime(date1));
					args.add(operateUtcTime(date2));

				} else if (Date_op_thisMonth.equals(op)) {

					Date date1 = DateUtil.firstDayOfMonth(new Date());
					Date date2 = DateUtils.addMonths(date1, 1);
					hql.append(" and ").append(propertyName).append(" >= ? and ").append(propertyName).append(" < ?");
					args.add(operateUtcTime(date1));
					args.add(operateUtcTime(date2));

				} else if (Date_op_never.equals(op)) {
					hql.append(" and ").append(propertyName).append(" is null");
				} else if (Date_op_between.equals(op) && StringUtils.isNotEmpty(value) && StringUtils.isNotEmpty(anotherValue)) {
					Date date1 = DateUtil.str2date(value);
					Date date2 = DateUtils.addDays(DateUtil.str2date(anotherValue), 1);
					
					hql.append(" and ").append(propertyName).append(" >= ? and ").append(propertyName).append(" < ?");
					
					args.add(operateUtcTime(date1));
					args.add(operateUtcTime(date2));
				}
		 }
	 }
	 
	 private Date operateUtcTime(Date date)
	{
		return DateUtils.addHours(date,-8);
	}
	 
	@SuppressWarnings("unchecked")
	public List<Object[]> findAll() {
		try {
			// return getHibernateTemplate().find("from HistoryLog");
			Query query = getSession().createQuery("select startTime,endTime,ipAddress,macAddress,hostName,memberOrServer,event,userName,protocol,DUID from HistoryLog order by startTime");
//			Query query = getSession().createQuery("from HistoryLog order by startTime");
			query.setFirstResult(1);
			query.setMaxResults(10000);
			return query.list();
		} catch (RuntimeException re) {
			throw re;
		}
	}
}
